package com.bestv.search.management.command;

import com.bestv.search.common.dataSource.DataSourceKey;
import com.bestv.search.common.dataSource.DataSourceKeyUtil;
import com.bestv.search.common.model.Info;
import com.bestv.search.common.model.SearchKeywordHistory;
import com.bestv.search.common.model.SysUser;
import com.bestv.search.common.model.TopNCache;
import com.bestv.search.common.service.InfoManager;
import com.bestv.search.common.service.SearchKeywordHistoryManager;
import com.bestv.search.common.service.TopNCacheManager;
import com.bestv.search.common.util.Excel;
import com.bestv.search.common.util.ExcelSheet;
import com.bestv.search.common.util.StringConst;
import com.bestv.search.management.servlet.BuildChartServlet;
import org.apache.log4j.Logger;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.jfree.chart.ChartUtilities;
import org.jfree.chart.JFreeChart;
import org.jfree.data.category.DefaultCategoryDataset;
import org.jfree.data.general.Dataset;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.*;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @author Justin Luo
 */
public class SearchKeywordHistoryCommand {
    private final String CHARTS_FOLDER = "charts";
    private final String dailyTitleSuffix = "日统计";
    private final String weeklyTitleSuffix = "周统计";
    private final String monthlyTitleSuffix = "月统计";
    private final String quarterTitleSuffix = "季度统计";
    private final String chartFormat = ".jpeg";
    private final int DEFAUL_TOPN_KEYWORD_LIMIT = 100;

    private Logger logger = Logger.getLogger(SearchKeywordHistoryCommand.class);
    private SearchKeywordHistoryManager searchKeywordHistoryManager;
    private TopNCacheManager topNCacheManager;
    private InfoManager infoManager;
    private InfoHeaderCommand infoHeaderCommand;
    private String chartsFolder;

    public void saveSearchKeywordHistorys(SearchKeywordHistory[] historys, String siteCode) {

        logger.info("Save searchkeyword history into DB started.");

        DataSourceKey key = DataSourceKeyUtil.convert(siteCode);
        searchKeywordHistoryManager.saveOrUpdateSearchKeywordHistory(key, historys);

        // Update hitCount to info
        try {
            List<Info> infos = new ArrayList<Info>();
            for (SearchKeywordHistory keyword : historys) {
                String mediaCode = keyword.getMediaCode();

                // Gets info index form infos list by mediaCode
                int index = getIndexByMediaCodeFromInfoList(mediaCode, infos);
                if (index == -1) {
                    // Doesn't exist this info in list, gets from DB
                    List<Info> infoList = infoManager.getInfoByCode(key, keyword.getMediaCode());
                    if (infoList == null || infoList.size() == 0)
                        continue;
                    Info info = infoList.get(0);
                    info.setHitCount(info.getHitCount() + keyword.getSearchCount());
                    info.setUpdateTime(new Date());
                    info.setUpdateTimeForIndex(new Date());
                    infos.add(info);
                } else {
                    Info info = infos.get(index);
                    info.setHitCount(info.getHitCount() + keyword.getSearchCount());
                    info.setUpdateTime(new Date());
                    info.setUpdateTimeForIndex(new Date());
                }

                if (infos.size() > 500) {
                    infoManager.saveUpdateInfoList(key, infos);

                    // Update InfoHeaders' updateTime and status
                    infoHeaderCommand.updateInfoHeadersTimeAndStatus(infos);

                    infos = new ArrayList<Info>();
                }
            }

            if (infos.size() > 0) {
                infoManager.saveUpdateInfoList(key, infos);

                // Update InfoHeaders' updateTime and status
                infoHeaderCommand.updateInfoHeadersTimeAndStatus(infos);
            }
        } catch (Exception e) {
            logger.error("Update hitCount into info failed, " + e.getMessage());
            e.printStackTrace();
        }

        logger.info("Save searchkeyword history into DB finished.");
    }

    private int getIndexByMediaCodeFromInfoList(String mediaCode, List<Info> infos) {
        if (mediaCode == null || infos == null || infos.size() == 0) {
            return -1;
        } else {
            for (int i = 0; i < infos.size(); i++) {
                if (mediaCode.equals(infos.get(i).getCode())) {
                    return i;
                }
            }
        }
        return -1;
    }

    public SearchKeywordHistory[] getSearchKeywordHistoryList(String filePath, String dsKey) {
        BufferedReader br = null;
        HashMap<String, SearchKeywordHistory> results = new HashMap<String, SearchKeywordHistory>();
        Set<String> mediaCodes = new HashSet<String>();
        try {
            br = new BufferedReader(new FileReader(filePath));
            String line;
            // each row is one search record
            while ((line = br.readLine()) != null) {
                SearchKeywordHistory record = new SearchKeywordHistory();

                // each row have four columns, they are dataTime, keyword,
                // userId, domain and split by ","
                String[] fields = line.split(",");
                String dateStr = fields[0].split(" ")[0];

                record.setDate(convert(dateStr));
                record.setKeyword(fields[1]);
                record.setLastUpdateTime(new Date());
                if (fields.length >= 5) {
                    record.setMediaCode(fields[4]);
                    mediaCodes.add(fields[4]);
                }

                // Checks key whether exist in result map.
                String key = record.getMediaCode() == null ? record.getKeyword() : (record.getMediaCode() + record
                        .getKeyword());
                SearchKeywordHistory recordInMap = results.get(key);
                if (recordInMap == null) {
                    // doesn't exist
                    record.setSearchCount(1);
                    results.put(key, record);
                } else {
                    // exists, update the count
                    recordInMap.setSearchCount(recordInMap.getSearchCount() + 1);
                }
            }
        } catch (IOException e) {
            logger.error("Gets SearchKeywordHistoryList failed form file : " + filePath);
            logger.error(e);
            e.printStackTrace();
        } finally {
            if (br != null)
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
        }

        SearchKeywordHistory[] array = new SearchKeywordHistory[results.size()];
        return results.values().toArray(array);
    }

    public String getTopNKeywordTOJSON(SysUser user, Map<String, String[]> map, DataSourceKey dsKey)
            throws JSONException {
        String[] topN = map.get("topN");
        if (topN == null) {
            logger.error("Incorrectly parameters, topN is null");
            return "{'results':'Error'}";
        }

        List<TopNCache> list = getTopNKeyword(user, map, dsKey);
        if (list == null) {
            return "{'results':'Error'}";
        }

        // Gets TopN record and converts to JSONArray object.
        JSONArray array = new JSONArray();
        int top = Integer.valueOf(topN[0]);
        top = list.size() > top ? top : list.size();
        for (int i = 0; i < top; i++) {
            JSONObject json = new JSONObject();
            TopNCache topNCache = list.get(i);
            json.put("rowNo", i + 1);
            json.put("keyword", topNCache.getName());
            json.put("searchCount", topNCache.getThisCount());
            json.put("lastRowNo", topNCache.getLastRankNo());
            json.put("lastSearchCount", topNCache.getLastCount());
            json.put("rasieNo", topNCache.getIncreaseNo());
            array.put(json);
        }
        return "{'results':" + array.toString() + "}";
    }

    public List<TopNCache> getTopNKeyword(SysUser user, Map<String, String[]> map, DataSourceKey dsKey) {
        String[] type = map.get("type");

        if (type == null) {
            logger.error("Incorrectly parameters, type is null");
            return null;
        }

        return getTopNKeyword(type[0], dsKey);
    }

    private List<TopNCache> getTopNKeyword(String type, DataSourceKey dsKey) {

        List<TopNCache> list = null;
        try {
            if (StringConst.DAILY.equals(type) || StringConst.WEEKLY.equals(type) || StringConst.MONTHLY.equals(type)
                    || StringConst.QUARTER.equals(type)) {
                list = topNCacheManager.get(dsKey, type, StringConst.KEYWORD, null, DEFAUL_TOPN_KEYWORD_LIMIT);

            } else {
                logger.error("Incorrectly parameters, type is " + type);
                return null;
            }
        } catch (Exception e) {
            logger.error("No DAO configuration found for site: " + dsKey.toString());
            e.printStackTrace();
            return null;
        }

        return list;
    }

    public HSSFWorkbook createExcel(SysUser user, Map<String, String[]> map, DataSourceKey dsKey) {
        String[] topN = map.get("topN");
        if (topN == null) {
            logger.error("Incorrectly parameters, topN is null");
            return null;
        }
        int top = Integer.valueOf(topN[0]);

        Map<String, String[]> paras = new HashMap<String, String[]>();
        paras.put("dataSourceKey", map.get("dataSourceKey"));

        List<ExcelSheet> sheets = new ArrayList<ExcelSheet>();

        // gets daily sheet
        paras.put("type", new String[]{"daily"});
        List<TopNCache> list = getTopNKeyword(user, paras, dsKey);
        if (list == null) {
            return null;
        }
        List<String> dailyHeader = new ArrayList<String>();
        dailyHeader.add("今日排行");
        dailyHeader.add("关键字");
        dailyHeader.add("今日搜索次数");
        dailyHeader.add("昨日排行");
        dailyHeader.add("昨日搜索次数");
        dailyHeader.add("今日提升名次");
        sheets.add(formatDataForExport(list, dailyHeader, "日排行", top));

        // gets week sheet
        paras.put("type", new String[]{"weekly"});
        list = getTopNKeyword(user, paras, dsKey);
        if (list == null) {
            return null;
        }
        List<String> weeklyHeader = new ArrayList<String>();
        weeklyHeader.add("本周排行");
        weeklyHeader.add("关键字");
        weeklyHeader.add("本周搜索次数");
        weeklyHeader.add("上周排行");
        weeklyHeader.add("上周搜索次数");
        weeklyHeader.add("本周提升名次");
        sheets.add(formatDataForExport(list, weeklyHeader, "周排行", top));

        // gets month sheet
        paras.put("type", new String[]{"monthly"});
        list = getTopNKeyword(user, paras, dsKey);
        if (list == null) {
            return null;
        }
        List<String> monthlyHeader = new ArrayList<String>();
        monthlyHeader.add("本月排行");
        monthlyHeader.add("关键字");
        monthlyHeader.add("本月搜索次数");
        monthlyHeader.add("上月排行");
        monthlyHeader.add("上月搜索次数");
        monthlyHeader.add("本月提升名次");
        sheets.add(formatDataForExport(list, monthlyHeader, "月排行", top));

        // gets quarter sheet
        paras.put("type", new String[]{"quarter"});
        list = getTopNKeyword(user, paras, dsKey);
        if (list == null) {
            return null;
        }
        List<String> quarterHeader = new ArrayList<String>();
        quarterHeader.add("本季排行");
        quarterHeader.add("关键字");
        quarterHeader.add("本季搜索次数");
        quarterHeader.add("上季排行");
        quarterHeader.add("上季搜索次数");
        quarterHeader.add("本季提升名次");
        sheets.add(formatDataForExport(list, quarterHeader, "季排行", top));

        // create excel file.
        Excel excel = new Excel();
        HSSFWorkbook book = excel.create(sheets);

        return book;
    }

    private ExcelSheet formatDataForExport(List<TopNCache> list, List<String> header, String sheetName, int topN) {
        ExcelSheet sheet = new ExcelSheet();
        sheet.setSheetName(sheetName);
        sheet.setDataHeader(header);

        List<String[]> data = new ArrayList<String[]>();
        topN = list.size() > topN ? topN : list.size();
        for (int i = 0; i < topN; i++) {
            TopNCache topNCache = list.get(i);
            String[] row = new String[6];
            row[0] = String.valueOf(i + 1);
            row[1] = topNCache.getName();
            row[2] = String.valueOf(topNCache.getThisCount());
            row[3] = String.valueOf(topNCache.getLastRankNo());
            row[4] = String.valueOf(topNCache.getLastCount());
            row[5] = String.valueOf(topNCache.getIncreaseNo());
            data.add(row);
        }
        sheet.setData(data);

        return sheet;
    }

    public String getChartURL(SysUser user, Map<String, String[]> map, DataSourceKey dsKey) {
        String[] type = map.get("type");
        String[] keyword = map.get("keyword");

        if (type == null) {
            logger.error("Incorrectly parameters, type is null");
            return null;
        }

        String key = "";
        if (keyword == null) {
            logger.error("Incorrectly parameters, keyword is null");
            return null;
        } else {
            try {
                key = new String(keyword[0].getBytes(StringConst.ISO_8859_1), StringConst.GBK);
            } catch (UnsupportedEncodingException e) {
                logger.error("Decode failed for keyword: " + keyword[0]);
            }
        }

        Date date = new Date();
        SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd");
        return CHARTS_FOLDER + File.separator + dsKey.toString() + File.separator + format.format(date)
                + File.separator + key.hashCode() + type[0] + chartFormat;
    }

    public void clearChartsForTopNKeyword(String siteCode) {
        String path = getChartsFolder(siteCode);
        File file = new File(path);

        // 删除该路径下所有文件
        File subFiles[] = file.listFiles();
        for (File subFile : subFiles) {
            deleteFile(subFile);
        }
    }

    private void deleteFile(File file) {
        if (file.exists()) {
            if (file.isFile()) {
                file.delete();
            } else if (file.isDirectory()) {
                File files[] = file.listFiles();
                for (int i = 0; i < files.length; i++) {
                    this.deleteFile(files[i]);
                }
            }
            file.delete();
        } else {
            logger.error("Can't find file " + file.getName());
        }
    }

    /**
     * generate all daily, weekly, monthly charts by keyword.
     */
    public void buildChartsForTopNKeyword(String siteCode) {
        DataSourceKey dsKey = DataSourceKeyUtil.convert(siteCode);

        // Gets all charts for daily keyword list.
        buildChartsForTopNKeyword(StringConst.DAILY, dsKey);
        // Gets all charts for weekly keyword list
        buildChartsForTopNKeyword(StringConst.WEEKLY, dsKey);
        // Gets all charts for monthly keyword list
        buildChartsForTopNKeyword(StringConst.MONTHLY, dsKey);
        // Gets all charts for quarter keyword list
        buildChartsForTopNKeyword(StringConst.QUARTER, dsKey);
    }

    private void buildChartsForTopNKeyword(String type, DataSourceKey dsKey) {
        Calendar dailyStart = Calendar.getInstance();
        dailyStart.add(Calendar.DAY_OF_YEAR, -15);

        Calendar weeklyStart = Calendar.getInstance();
        weeklyStart.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
        weeklyStart.add(Calendar.DAY_OF_YEAR, -98);

        Calendar monthlyStart = Calendar.getInstance();
        monthlyStart.set(Calendar.DAY_OF_MONTH, 1);
        monthlyStart.add(Calendar.MONTH, -12);

        Calendar quarterStart = Calendar.getInstance();
        quarterStart.set(Calendar.DAY_OF_MONTH, 1);
        quarterStart.add(Calendar.MONTH, -36);

        Calendar end = Calendar.getInstance();
        end.add(Calendar.DAY_OF_YEAR, -1);

        // 获取保存charts的子目录
        Date date = new Date();
        SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd");

        String path = getChartsFolder(dsKey.toString());
        // 创建日期子文件夹
        path = path + File.separator + format.format(date);
        File file = new File(path);
        if (!file.exists())
            file.mkdir();

        // Gets all charts for keyword list.
        List<TopNCache> list = getTopNKeyword(type, dsKey);
        for (TopNCache topNCache : list) {
            // Create chart as JEPG for daily.
            saveChartAsJPEG(topNCache.getName(), path, dailyTitleSuffix, dailyStart.getTime(), end.getTime(),
                    StringConst.DAILY, dsKey.toString());
            // Create chart as JEPG for weekly.
            saveChartAsJPEG(topNCache.getName(), path, weeklyTitleSuffix, weeklyStart.getTime(), end.getTime(),
                    StringConst.WEEKLY, dsKey.toString());
            // Create chart as JEPG for monthly.
            saveChartAsJPEG(topNCache.getName(), path, monthlyTitleSuffix, monthlyStart.getTime(), end.getTime(),
                    StringConst.MONTHLY, dsKey.toString());
            // Create chart as JEPG for quarter.
            saveChartAsJPEG(topNCache.getName(), path, quarterTitleSuffix, quarterStart.getTime(), end.getTime(),
                    StringConst.QUARTER, dsKey.toString());
        }
    }

    private Dataset getDataset(String keyword, Date startDate, Date endDate, String type, String sc) {

        Dataset dataset = new DefaultCategoryDataset();
        DataSourceKey key = DataSourceKeyUtil.convert(sc);
        List<String[]> list = searchKeywordHistoryManager.getSearchKeywordHistoryByKeyword(key, keyword, startDate,
                endDate, type);
        for (String[] row : list) {
            ((DefaultCategoryDataset) dataset).setValue(Integer.valueOf(row[2]), "", row[1]);
        }
        return dataset;
    }

    private void saveChartAsJPEG(String keyword, String filePath, String fileSuffix, Date startDate, Date endDate,
                                 String type, String sc) {
        // Create chart and save as JPEG
        String titleHeader = keyword;
        String fileFullName = filePath + File.separator + Math.abs(titleHeader.hashCode()) + type + chartFormat;
        File file = new File(fileFullName);
        if (!file.exists()) {
            Dataset dataset = getDataset(keyword, startDate, endDate, type, sc);
            JFreeChart chart;
            try {
                file.createNewFile();
                chart = BuildChartServlet.createChart(dataset, titleHeader + fileSuffix, "", "", "keywordHistory",
                        "line");
                ChartUtilities.saveChartAsJPEG(file, chart, 900, 450);
            } catch (IOException e) {
                logger.error("Generate chart field for keyword: " + keyword);
                logger.error(e);
                e.printStackTrace();
            }
        }
    }

    private String getChartsFolder(String siteCode) {
        if (chartsFolder == null) {
            chartsFolder = SearchKeywordHistoryCommand.class.getResource("/").getPath();
            chartsFolder = new File(chartsFolder).getParentFile().getParentFile().getPath();
            if (!chartsFolder.endsWith(File.separator))
                chartsFolder = chartsFolder + File.separator;

            // 创建保存charts的目录
            chartsFolder = chartsFolder + File.separator + CHARTS_FOLDER;
            File file = new File(chartsFolder);
            if (!file.exists())
                file.mkdir();

            // 创建驻地子文件夹
            chartsFolder = chartsFolder + File.separator + siteCode;
            file = new File(chartsFolder);
            if (!file.exists())
                file.mkdir();
        }
        return chartsFolder;
    }

    private Date convert(String dateStr) {
        Calendar calendar = Calendar.getInstance();
        calendar.set(Integer.valueOf(dateStr.substring(0, 4)), Integer.valueOf(dateStr.substring(5, 7)) - 1,
                Integer.valueOf(dateStr.substring(8)));
        return calendar.getTime();
    }

    public SearchKeywordHistoryManager getSearchKeywordHistoryManager() {
        return searchKeywordHistoryManager;
    }

    public void setSearchKeywordHistoryManager(SearchKeywordHistoryManager searchKeywordHistoryManager) {
        this.searchKeywordHistoryManager = searchKeywordHistoryManager;
    }

    public TopNCacheManager getTopNCacheManager() {
        return topNCacheManager;
    }

    public void setTopNCacheManager(TopNCacheManager topNCacheManager) {
        this.topNCacheManager = topNCacheManager;
    }

    public InfoManager getInfoManager() {
        return infoManager;
    }

    public void setInfoManager(InfoManager infoManager) {
        this.infoManager = infoManager;
    }

    public InfoHeaderCommand getInfoHeaderCommand() {
        return infoHeaderCommand;
    }

    public void setInfoHeaderCommand(InfoHeaderCommand infoHeaderCommand) {
        this.infoHeaderCommand = infoHeaderCommand;
    }
}